﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Threading;
using System.Diagnostics;
using System.Windows.Forms;

namespace VntNet.PowerSchemeSwitcher
{
    internal class PowerLineHelper
    {
        const int LoopInterval=30000;
        int _prevBatteryLifePercent;
        bool _exit;
        ManualResetEvent _checkBatteryEvent;
        internal enum PowerLineStatus : byte
        {
            Battery = 0,
            AC = 1,
            Unknown = 255
        }

        public event Action<PowerLineStatus> PowerLineStatusChanged;
        public event Action<int> BatteryLifePercentChanged;

        public PowerLineHelper()
        {
            _checkBatteryEvent = new ManualResetEvent(true);
            _exit = false;
            _prevBatteryLifePercent = 0;
            SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
            Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
            ThreadPool.QueueUserWorkItem(delegate { MonitorBatteryStatusThreadFn(); });
        }

        void Application_ApplicationExit(object sender, EventArgs e)
        {
            _exit = true;
        }

        void MonitorBatteryStatusThreadFn() 
        {
            while (!_exit)
            {
                _checkBatteryEvent.WaitOne(LoopInterval);
                _checkBatteryEvent.Reset();
                try
                {
                    MonitorBatteryStatusInternal();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                    //throw;
                }
            }
        }

        internal void ForceBatteryStatuCheck()
        {
            _checkBatteryEvent.Set();
        }

        void MonitorBatteryStatusInternal()
        {
            int BatteryLifePercent = 0;
            if (GetCurrentSystemPowerStatus() == PowerLineStatus.Battery)
            {
                BatteryLifePercent = PowerLineNativeHelper.GetCurrentBatteryLifePercent();
                if (BatteryLifePercent!=-1 && BatteryLifePercent != _prevBatteryLifePercent)
                {
                    OnBatteryLifePercentChanged(BatteryLifePercent);
                    _prevBatteryLifePercent = BatteryLifePercent;
                }
            }
        }

        void OnBatteryLifePercentChanged(int batteryLifePercent)
        {
            if (BatteryLifePercentChanged != null)
                BatteryLifePercentChanged(batteryLifePercent);
        }

        void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
        {            
            if(PowerLineStatusChanged!=null)
                PowerLineStatusChanged((PowerLineStatus)e.Mode);
            if (PowerLineNativeHelper.GetCurrentSystemPowerLineStatus() == PowerLineStatus.Battery)
            {
                //ensure that we immediatly check the battery staus
                ForceBatteryStatuCheck();
            }
        }



        internal static PowerLineStatus GetCurrentSystemPowerStatus()
        {
            return PowerLineNativeHelper.GetCurrentSystemPowerLineStatus();
        }

        internal static int GetCurrentBatteryLifePercent()
        {
            return PowerLineNativeHelper.GetCurrentBatteryLifePercent();
        }

        class PowerLineNativeHelper
        {
            [Flags]
            enum BatteryFlag : byte
            {
                High = 1,
                Low = 2,
                Critical = 4,
                Charging = 8,
                NoSystemBattery = 128,
                Unknown = 255
            }

            [StructLayout(LayoutKind.Sequential)]
            struct SystemPowerStatus
            {
                internal PowerLineStatus PowerLineStatus;
                internal BatteryFlag BatteryFlag;
                internal byte BatteryLifePercent;
                internal byte Reserved1;
                internal uint BatteryLifeTime;//number of second
                internal uint BatteryFullLifeTime;
            }

            [DllImport("kernel32")]
            static extern bool GetSystemPowerStatus(out SystemPowerStatus sps);

            public static PowerLineStatus GetCurrentSystemPowerLineStatus()
            {
                SystemPowerStatus SysPowerStartus;
                if(GetSystemPowerStatus(out SysPowerStartus))
                    return SysPowerStartus.PowerLineStatus;
                else
                    return PowerLineStatus.Unknown;
            }

            public static int GetCurrentBatteryLifePercent()
            {
                SystemPowerStatus SysPowerStartus;
                if (GetSystemPowerStatus(out SysPowerStartus))
                    return (int)SysPowerStartus.BatteryLifePercent;
                else
                    return -1;
            }
        }

    }
}
